﻿Imports System.Collections.ObjectModel
Imports System.Data
Imports System.Data.OleDb
Imports System.IO
Imports System.IO.Ports
Imports System.Text
Imports System.Threading
Imports System.Windows.Threading
Imports FL_8x0.Common
Imports FL_8x0.FL_830.Model
Imports FL_8x0.FL_830.View
Imports FL_8x0.Model
Imports FL_8x0.View
Imports GalaSoft.MvvmLight
Imports GalaSoft.MvvmLight.CommandWpf
Imports GalaSoft.MvvmLight.Messaging
Imports GalaSoft.MvvmLight.Threading
Imports Microsoft.Win32
Imports OxyPlot

Namespace FL_8x0.FL_830.ViewModel

    Public Class LeakTestViewModel
        Inherits ViewModelBase

        Private _gp1 As String            '直压1
        Private _gp2 As String            '直压2
        Private _dp As String             '差压
        Private _temp As String           '温度
        Private _leak As String           '漏率
        Private _errorCode As String      '错误码

        Private _recordList As DataView   '测试数据

        Private _repeat As Boolean        '重复检测
        Private _repeatNum As UShort      '重复次数


        Private _createReportCommand As RelayCommand      '点击“新建报告” 触发命令（事件）
        Private _exportReportCommand As RelayCommand      '点击“导出报告” 触发命令（事件）

        Private _showConnectInfoCommand As RelayCommand   '点击“连接模式” 触发命令（事件）
        Private _connectCommand As RelayCommand           '点击“连接/断开” 触发命令（事件）

        Private _zeroCommand As RelayCommand              '点击“零点清除” 触发命令（事件）
        Private _clearErrorCommand As RelayCommand        '点击“错误清除” 触发命令（事件）

        Private _startCommand As RelayCommand             '点击“启动” 触发命令（事件）
        Private _stopCommand As RelayCommand              '点击“停止” 触发命令（事件）
        Private _operationText As String                  '操作按钮显示文字  连接/断开
        Private _saveWaveCommand As RelayCommand          '点击“导出曲线” 触发命令（事件）

        Private _enableButton As Boolean                  '新建报告、停止 按钮状态
        Private _enableStart As Boolean                   '启动状态
        Private _dpColor As Brush                         '差压文本颜色

        Private _startMode As String = "Leak"             '启动模式

        Private ReadOnly _timer As DispatcherTimer        '定时任务

        Private _isStart As Boolean = False                  '已经启动检测

        Private _stateBackColorList As ObservableCollection(Of Brush)   '"准备"、"运行"、"停止" 三状态背景色
        Private _stateFontColorList As ObservableCollection(Of Brush)   '"准备"、"运行"、"停止" 三状态文字颜色

        Private _linkInfoList As ObservableCollection(Of LinkModel)       'ready，延时,充气，平衡，检漏，排气 五状态信息
        Private _resultBackColorList As ObservableCollection(Of Brush)    'OK NG 二状态色
        Private _chartModel As PlotModel                                  '曲线

        Private ReadOnly _nsMain As New NumberStates  '1级 状态机（控制主时序）
        '                                   （-1=ready，0=延时,1=充气，2=平衡，3=检漏，4=排气）
        Private Const MIN_nsMain = -1
        Private Const MAX_nsMain = 4

        Private ReadOnly _dt As DataTable = New DataTable  '测试表格

        Private ReadOnly _waveX = New List(Of Single)       'X轴点数据
        Private ReadOnly _waveY0 = New List(Of Single)      '直压点数据
        Private ReadOnly _waveY1 = New List(Of Single)      '差压点数据

        Private ReadOnly _unActiveColor As Brush = New SolidColorBrush(Color.FromRgb(230, 230, 230))  '不活动时颜色
        Private ReadOnly _activeColor As Brush = New SolidColorBrush(Color.FromRgb(38, 73, 154))      '活动时颜色

        Private ReadOnly _whiteColor As Brush = New SolidColorBrush(Colors.White)      '白色
        Private ReadOnly _blackColor As Brush = New SolidColorBrush(Colors.Black)      '黑色


        Private ReadOnly _normalColor As Brush = New SolidColorBrush(ColorConverter.ConvertFromString("#808080")) '不可用颜色
        Private ReadOnly _ngColor As Brush = New SolidColorBrush(ColorConverter.ConvertFromString("#EB3636"))     'NG色
        Private ReadOnly _okColor As Brush = New SolidColorBrush(ColorConverter.ConvertFromString("#99FF00"))     'OK色

        Private _startModeCommand As RelayCommand(Of String)    '点击“启动模式”触发命令（事件）
        Private _ngResult As String                             'NG时具体NG结果

        Private _minY1 As Double
        Private _maxY1 As Double
        Private _minY2 As Double
        Private _maxY2 As Double
        Private _yLabel As String
        Private _gpPoints As ObservableCollection(Of DataPoint) '直压曲线
        Private _dpPoints As ObservableCollection(Of DataPoint) '差压曲线

        Private _lngIdPre As Integer = -1                           'id 前一个值
        Private ReadOnly _threadLock As Object = New Object()
        Private _lngGroupIdPre As Integer = -1                      '组号 前一个值

        Private _curRow As DataRow                              '最新数据行


        Private _lngTimeTick As Integer = 0           '时间戳
        Private _lngAutoStartTime As Integer = 0     '自动启动定时

        Private _repeatInterval As UShort

        Private _sngAxisInterval As Single = 5        '

        Private _ucProcessPre As UShort = 0

        Private _productName As String
        Private _productCode As String
        Private _productCodeBuffer As String


        Public Sub New()
            CreateReportCommand = New RelayCommand(Sub() ShowReportDialog())
            ExportReportCommand = New RelayCommand(Sub() SaveResult())
            ShowConnectInfoCommand = New RelayCommand(Sub() Call New ConnectInfo().ShowDialog())
            ConnectCommand = New RelayCommand(Sub() Operation())
            OperationText = IIf(uLPU.IsServerRunning, "断开", "连接")

            ZeroCommand = New RelayCommand(Sub() uLPU.OUT_WriteUint16(1, ADDR_OUT_CONTROL1, uLPU.OUT_ReadUint16(1, ADDR_OUT_CONTROL1) Or &H4000))
            ClearErrorCommand = New RelayCommand(Sub() uLPU.OUT_WriteUint16(1, ADDR_OUT_CONTROL1, uLPU.OUT_ReadUint16(1, ADDR_OUT_CONTROL1) Or &H8000))

            StartModeCommand = New RelayCommand(Of String)(Sub(mode As String) _startMode = mode)
            StartCommand = New RelayCommand(AddressOf Start)
            StopCommand = New RelayCommand(Sub()
                                               uLPU.OUT_WriteUint16(1, ADDR_OUT_CONTROL1, uLPU.OUT_ReadUint16(1, ADDR_OUT_CONTROL1) Or &H2)
                                               Table_DelBlankLine()
                                           End Sub)
            SaveWaveCommand = New RelayCommand(AddressOf SaveWave)


            _timer = New DispatcherTimer
            _timer.Interval = TimeSpan.FromMilliseconds(300)
            AddHandler _timer.Tick, AddressOf FlashView
            _timer.IsEnabled = uLPU.IsServerRunning
            EnableButton = uLPU.IsServerRunning()

            _nsMain.Init(MIN_nsMain, MAX_nsMain)

            InitColorStatus()

            _ngResult = "NG"
            For i = 0 To g_strResultTitle.Count - 1
                _dt.Columns.Add(New DataColumn(g_strResultTitle(i)))
            Next

            Dim dv As New DataView(_dt)
            RecordList = dv

            _lngTimeTick = My.Computer.Clock.TickCount
            GpPoints = New ObservableCollection(Of DataPoint)()
            DpPoints = New ObservableCollection(Of DataPoint)()
            _yLabel = "直压(kPa)"

            Messenger.Default.Register(Me, "update_product", Sub(msg As String)
                                                                 g_udtReport.ReportId = SystemData.idWorker.nextId()
                                                                 User_AddReport(g_udtReport)
                                                                 g_udtProduct = User_ReadProduct(g_udtProduct.Id)
                                                                 YLabel = " 直压(" & g_strGPUnit(g_udtProduct.GpUnitIndex) & ")"
                                                             End Sub)

            'CreateChart()
        End Sub


        '=====================================================================
        '函 数 名: CreateChart
        '参    数: 
        '返 回 值:
        '功能描述: 创建曲线
        '=====================================================================
        'Private Sub CreateChart()

        '    ChartModel = New PlotModel
        '    Dim gpAxis As New LinearAxis()
        '    gpAxis.Title = "直压(kPa)"
        '    gpAxis.Key = "gp"
        '    gpAxis.Minimum = -5
        '    gpAxis.Maximum = 5
        '    gpAxis.IsZoomEnabled = False

        '    Dim dpAxis As New LinearAxis()
        '    dpAxis.Title = "差压(Pa)"
        '    dpAxis.Key = "dp"
        '    dpAxis.Position = AxisPosition.Right
        '    dpAxis.Minimum = -5
        '    dpAxis.Maximum = 5
        '    dpAxis.IsPanEnabled = False
        '    dpAxis.IsZoomEnabled = False

        '    Dim timeAxis As New LinearAxis()
        '    timeAxis.Title = "时间(S)"
        '    timeAxis.Key = "time"
        '    timeAxis.Position = AxisPosition.Bottom
        '    timeAxis.Minimum = 0
        '    timeAxis.IsZoomEnabled = False

        '    ChartModel.Axes.Add(gpAxis)
        '    ChartModel.Axes.Add(dpAxis)
        '    ChartModel.Axes.Add(timeAxis)

        '    Dim legend = New Legend() With {
        '            .LegendPosition = LegendPosition.LeftTop,
        '            .Key = "ll"}
        '    ChartModel.Legends.Add(legend)

        '    _gpSeries = New LineSeries() With {
        '        .Title = "测试压",
        '        .XAxisKey = "time",
        '        .YAxisKey = "gp",
        '        .LegendKey = "ll",
        '        .Color = OxyColors.Blue
        '        }
        '    _dpSeries = New LineSeries() With {
        '        .Title = "差压",
        '        .XAxisKey = "time",
        '        .YAxisKey = "dp",
        '        .LegendKey = "ll",
        '        .Color = OxyColors.Red
        '        }
        '    ChartModel.Series.Add(_gpSeries)
        '    ChartModel.Series.Add(_dpSeries)
        'End Sub


        '=====================================================================
        '函 数 名: InitColorStatus
        '参    数: 
        '返 回 值:
        '功能描述: 初始化状态和环节默认颜色
        '=====================================================================
        Private Sub InitColorStatus()

            StateBackColorList = New ObservableCollection(Of Brush) From {
                _unActiveColor, _unActiveColor, _unActiveColor
                }
            StateFontColorList = New ObservableCollection(Of Brush) From {
                _blackColor, _blackColor, _blackColor
                }

            ResultBackColorList = New ObservableCollection(Of Brush) From {
                _normalColor, _normalColor
                }


            LinkInfoList = New ObservableCollection(Of LinkModel) From {
                New LinkModel With {
                    .Title = "准备环节",
                    .TitleColor = _blackColor,
                    .BackColor = _unActiveColor,
                    .LinkTitleColor = _unActiveColor
                    },
                New LinkModel With {
                    .Title = "延时环节",
                    .TitleColor = _blackColor,
                    .BackColor = _unActiveColor,
                    .LinkTitleColor = _unActiveColor
                    },
                New LinkModel With {
                    .Title = "充气环节",
                    .TitleColor = _blackColor,
                    .BackColor = _unActiveColor,
                    .LinkTitleColor = _unActiveColor
                    },
                New LinkModel With {
                    .Title = "平衡环节",
                    .TitleColor = _blackColor,
                    .BackColor = _unActiveColor,
                    .LinkTitleColor = _unActiveColor
                    },
                New LinkModel With {
                    .Title = "检测环节",
                    .TitleColor = _blackColor,
                    .BackColor = _unActiveColor,
                    .LinkTitleColor = _unActiveColor
                    },
                New LinkModel With {
                    .Title = "排气环节",
                    .TitleColor = _blackColor,
                    .BackColor = _unActiveColor,
                    .LinkTitleColor = _unActiveColor
                    }
                }
        End Sub


        '=====================================================================
        '函 数 名: SaveWave
        '参    数: 
        '返 回 值:
        '功能描述: （保存曲线）
        '=====================================================================
        Private Sub SaveWave()
            If RecordList.Count > 0 Then  '有测试记录
                '用户保存
                Dim saveDialog As New SaveFileDialog With {
                        .Filter = "CSV文件|*.csv|所有文件|*.*",
                        .FileName = ""
                        }
                saveDialog.ShowDialog()

                If saveDialog.FileName <> "" Then
                    SaveWaveCsv(saveDialog.FileName)  '保存曲线记录
                End If
            Else
                log.WriteLogError("保存曲线", "曲线无数据，无法导出")
            End If
        End Sub

        '======================================================================================
        '函 数 名: SaveWaveCsv
        '参    数: 
        '返 回 值: 
        '功能描述: 保存曲线到CSV文本文件
        '======================================================================================
        Private Sub SaveWaveCsv(strFileName As String)
            If _waveX.Count > 0 Then  '有测试记录
                If Directory.Exists(g_udtOtherSet.strAutoSaveWavePath) = False Then '文件夹不存在
                    Directory.CreateDirectory(g_udtOtherSet.strAutoSaveWavePath)
                End If

                Dim swFile As StreamWriter = New StreamWriter(strFileName, False, Encoding.Default)

                swFile.Write("时间(s),  测试压(" & g_strGPUnit(g_udtProduct.GpUnitIndex) & "), 差压(Pa)" & vbCrLf)

                Dim i As UInt16

                Dim strRow As String

                Try
                    For i = 0 To _waveX.Count - 1
                        strRow = CStr(_waveX(i)) & ", "
                        strRow &= CStr(_waveY0(i)) & ", "
                        strRow &= CStr(_waveY1(i))

                        swFile.Write(strRow & vbCrLf)
                    Next
                Catch ex As Exception
                    log.WriteLogError("SaveWaveCsv():" & strFileName, ex.Message)
                End Try

                swFile.Close()
            End If
        End Sub

        '======================================================================================
        '函 数 名: ChartInit
        '参    数: 
        '返 回 值: 
        '功能描述: 设置坐标轴极值，清空点数据
        '======================================================================================
        Private Sub ChartInit()
            '直压 坐标轴极限
            If g_udtOtherSet.blnAutoAxisY Then
                MinY1 = -_sngAxisInterval
                MaxY1 = _sngAxisInterval
            Else
                MinY1 = g_udtOtherSet.sngMinAxisY
                MaxY1 = g_udtOtherSet.sngMaxAxisY
            End If

            '差压 坐标轴极限
            If g_udtOtherSet.blnAutoAxisY2 Then
                MinY2 = -_sngAxisInterval
                MaxY2 = _sngAxisInterval
            Else
                MinY2 = g_udtOtherSet.sngMinAxisY2
                MaxY2 = g_udtOtherSet.sngMaxAxisY2
            End If

            GpPoints.Clear()
            DpPoints.Clear()

            _waveX.Clear()
            _waveY0.Clear()
            _waveY1.Clear()
            _lngTimeTick = My.Computer.Clock.TickCount
        End Sub


        '======================================================================================
        '函 数 名: ChartAddPoint
        '参    数: 
        '返 回 值: 
        '功能描述: 曲线中添加一个直、差压数据点
        '======================================================================================
        Private Sub ChartAddPoint()
            Dim sigGP As Single '直压
            Dim sigDP As Single '差压

            sigGP = uLPU.IN_ReadSingle(1, ADDR_IN_GP2_USER_VALUE)
            sigDP = uLPU.IN_ReadSingle(1, ADDR_IN_DP_USER_VALUE)

            '实时修正差压
            If uLPU.OUT_ReadUint16(1, ADDR_OUT_SDET_UNIT) = 0 Then '单位为Pa
                Dim uiProcess As UInt16
                uiProcess = uLPU.IN_ReadUint16(1, ADDR_IN_STATES3)

                If uiProcess = 7 Then '小漏检测环节
                    If uLPU.OUT_ReadUint16(1, ADDR_OUT_SDET_OFFICE_EN) <> 0 Then
                        sigDP = sigDP * uLPU.OUT_ReadSingle(1, ADDR_OUT_SDET_LINEAR_K) -
                                uLPU.OUT_ReadSingle(1, ADDR_OUT_SDET_OFFICE_VALUE)
                    End If
                ElseIf uiProcess = 8 Then '大漏检测环节
                    If uLPU.OUT_ReadUint16(1, ADDR_OUT_LDET_OFFICE_EN) <> 0 Then
                        sigDP = sigDP * uLPU.OUT_ReadSingle(1, ADDR_OUT_LDET_LINEAR_K) -
                                uLPU.OUT_ReadSingle(1, ADDR_OUT_LDET_OFFICE_VALUE)
                    End If
                End If
            End If

            Dim time1 As Single = CSng(My.Computer.Clock.TickCount - _lngTimeTick) / 1000  'sec

            Dim sngTmp As Single

            If g_udtOtherSet.blnAutoAxisY Then
                sngTmp = MaxY1
                If sigGP > sngTmp Then
                    sngTmp = CLng((sigGP / _sngAxisInterval) + 1) * _sngAxisInterval
                End If
                If sngTmp < _sngAxisInterval Then sngTmp = _sngAxisInterval
                MaxY1 = sngTmp


                sngTmp = MinY1
                If sigGP < sngTmp Then
                    sngTmp = CLng((sigGP / _sngAxisInterval) - 1) * _sngAxisInterval
                End If
                If sngTmp > -_sngAxisInterval Then
                    sngTmp = -_sngAxisInterval
                End If
                MinY1 = sngTmp
            End If

            If g_udtOtherSet.blnAutoAxisY2 Then
                '自动调整Y2轴
                sngTmp = MaxY2
                If sigDP > sngTmp Then
                    sngTmp = CLng((sigDP / _sngAxisInterval) + 1) * _sngAxisInterval
                End If
                If sngTmp < _sngAxisInterval Then sngTmp = _sngAxisInterval
                MaxY2 = sngTmp

                sngTmp = MinY2
                If sigDP < sngTmp Then
                    sngTmp = CLng((sigDP / _sngAxisInterval) - 1) * _sngAxisInterval
                End If
                If sngTmp > -_sngAxisInterval Then
                    sngTmp = -_sngAxisInterval
                End If
                MinY2 = sngTmp
            End If

            _waveX.Add(time1)
            _waveY0.Add(sigGP)
            _waveY1.Add(sigDP)

            GpPoints.Add(New DataPoint(time1, sigGP))
            DpPoints.Add(New DataPoint(time1, sigDP))
        End Sub

        '=====================================================================
        '函 数 名: GetLPU_Process
        '参    数: 
        '返 回 值:
        '功能描述: 读取LPU当前检漏环节
        '=====================================================================
        Private Function GetLPU_Process() As UShort

            'LPU当前环节
            Dim ucPress As UShort = uLPU.IN_ReadUint16(1, ADDR_IN_STATES3)   'LPU当前环节
            If ucPress < 0 Then ucPress = 0
            If ucPress > g_strPress.Count - 1 Then ucPress = g_strPress.Count - 1

            Return ucPress
        End Function

        '=====================================================================
        '函 数 名: FlashView
        '参    数: 
        '返 回 值:
        '功能描述: 刷新界面显示
        '=====================================================================
        Private Sub FlashView()

            Dim ucPress As UInt16 = GetLPU_Process()   'LPU当前环节
            EnableButton = uLPU.IsModbusOK()

            Gp1 = Format(uLPU.IN_ReadSingle(1, ADDR_IN_GP1_USER_VALUE), g_strPoint(g_udtOtherSet.uint直压_Point)) & " " &
                  g_strGPUnit(uLPU.OUT_ReadUint16(1, ADDR_OUT_GP_UNIT))
            Gp2 = Format(uLPU.IN_ReadSingle(1, ADDR_IN_GP2_USER_VALUE), g_strPoint(g_udtOtherSet.uint直压_Point)) & " " &
                  g_strGPUnit(uLPU.OUT_ReadUint16(1, ADDR_OUT_GP_UNIT))
            Dp = Format(uLPU.IN_ReadSingle(1, ADDR_IN_DP_USER_VALUE), "0.0") & " Pa"
            Temp = Format(uLPU.IN_ReadSingle(1, ADDR_IN_TEMP_USER_VALUE), "0.0") & " ℃"

            If InStr(g_strTestType(uLPU.IN_ReadUint16(1, ADDR_IN_TEST_TYPE)), "CAL") > 0 Then
                Leak = Format(uLPU.IN_ReadSingle(1, ADDR_IN_LEAK), "0.0") & " " & "Pa/s"
            Else
                If uLPU.OUT_ReadUint16(1, ADDR_OUT_SDET_UNIT) = 1 Then
                    Leak = Format(uLPU.IN_ReadSingle(1, ADDR_IN_LEAK), g_strEPoint(g_udtOtherSet.uint漏率_Point)) & " " &
                           g_strLeakUnit(uLPU.OUT_ReadUint16(1, ADDR_OUT_GP_UNIT))
                Else
                    Leak = Format(uLPU.IN_ReadSingle(1, ADDR_IN_LEAK), g_strEPoint(g_udtOtherSet.uint漏率_Point)) & " " &
                           g_strLeakUnit(uLPU.OUT_ReadUint16(1, ADDR_OUT_SDET_UNIT))
                End If
            End If


            If ucPress = 6 Or ucPress = 7 Then
                DpColor = _blackColor
            Else
                DpColor = _whiteColor
            End If


            If _curRow IsNot Nothing Then
                '(17) = "充气2结束差压"
                If ucPress = 4 Then '(4) = "充气2"
                    _curRow(16) = Format(uLPU.IN_ReadSingle(1, ADDR_IN_DP_USER_VALUE), "0.0")
                End If

                '(18) = "平衡2结束差压"
                If ucPress = 5 Or ucPress = 6 Then '(5) = "平衡1"   (6) = "平衡2"
                    _curRow(17) = Format(uLPU.IN_ReadSingle(1, ADDR_IN_DP_USER_VALUE), "0.0")
                End If

                If ucPress = 7 Then '(7) = "小漏"
                    _curRow(7) = Format(uLPU.IN_ReadSingle(1, ADDR_IN_DP_USER_VALUE), "0.0")
                End If

                If ucPress = 8 Then '(8) = "大漏"
                    _curRow(10) = Format(uLPU.IN_ReadSingle(1, ADDR_IN_DP_USER_VALUE), "0.0")
                End If
            End If

            If ucPress = 0 Or ucPress = 1 Then
                _nsMain.ChangeReady()
            ElseIf ucPress = 2 Or ucPress = 9 Or ucPress = 11 Then
                _nsMain.Change(0)
            ElseIf ucPress = 3 Or ucPress = 4 Or ucPress = 12 Then
                _nsMain.Change(1)
            ElseIf ucPress = 5 Or ucPress = 6 Then
                _nsMain.Change(2)
            ElseIf ucPress = 7 Or ucPress = 8 Or ucPress = 13 Or ucPress = 14 Or ucPress = 15 Then
                _nsMain.Change(3)
            ElseIf ucPress = 10 Then
                _nsMain.Change(4)
            End If

            Dim intState As UInt16

            '运行状态
            intState = uLPU.IN_ReadUint16(1, ADDR_IN_STATES1)
            StateBackColorList(0) = IIf((intState And &H1) <> 0, _activeColor, _unActiveColor)
            StateBackColorList(1) = IIf((intState And &H2) <> 0, _activeColor, _unActiveColor)
            StateBackColorList(2) = IIf((intState And &H4) <> 0, _activeColor, _unActiveColor)

            StateFontColorList(0) = IIf((intState And &H1) <> 0, _whiteColor, _blackColor)
            StateFontColorList(1) = IIf((intState And &H2) <> 0, _whiteColor, _blackColor)
            StateFontColorList(2) = IIf((intState And &H4) <> 0, _whiteColor, _blackColor)


            '错误号
            Dim er As UInt16
            er = uLPU.IN_ReadUint16(1, ADDR_IN_ERROR)
            ErrorCode = "[" & er & "]" & g_strErrorMsg(er)

            Dim testType As UInt16 = uLPU.IN_ReadUint16(1, ADDR_IN_TEST_TYPE)
            For i = -1 To LinkInfoList.Count - 2
                If _nsMain.GetStates() <> i Then
                    LinkInfoList(i + 1).TitleColor = _blackColor
                    LinkInfoList(i + 1).BackColor = _unActiveColor
                    LinkInfoList(i + 1).LinkTitleColor = _unActiveColor
                Else
                    LinkInfoList(i + 1).TitleColor = _whiteColor
                    LinkInfoList(i + 1).BackColor = _activeColor
                    LinkInfoList(i + 1).LinkTitle = g_strPress(ucPress)
                    If testType = 13 Or (testType = 8 And i = 1) Or (Not Repeat And i = -1) Then
                        LinkInfoList(i + 1).Interval = ""
                    Else
                        LinkInfoList(i + 1).Interval = "剩余 " & uLPU.IN_ReadInt16(1, ADDR_IN_TIMER) & " S"
                    End If
                    LinkInfoList(i + 1).LinkTitleColor = _whiteColor
                End If
            Next

            Dim tmp1 As Integer
            tmp1 = uLPU.IN_ReadUint16(1, ADDR_IN_RECORD_ID)

            Dim intStart As UInt16 = uLPU.IN_ReadUint16(1, ADDR_IN_STATES1)

            If _ucProcessPre <> ucPress Then
                If Math.Abs(g_udtProduct.Times(9) - 0) > 0.000001 Then
                    If _ucProcessPre = 11 Then
                        _isStart = False
                    End If
                Else
                    If _ucProcessPre = 10 Then
                        _isStart = False
                    End If
                End If
                _ucProcessPre = ucPress
            End If

            If Not _isStart Then
                If intStart And &H2 Then
                    If IsNothing(g_udtReport.ReportId) Or g_udtReport.ReportId = "" Then
                        log.WriteLogInfo("泄漏检测", "自动添加最新记录")
                        SaveProductAndCreateReport()
                        RecordList.Table.Clear()
                    End If
                    ChartInit()
                    Table_AddNewLine()
                    _isStart = True
                    If Volatile.Read(_lngIdPre) = -1 Then
                        Volatile.Write(_lngIdPre, tmp1)
                    End If
                End If
            End If

            ' 线程同步
            SyncLock _threadLock
                '有新记录
                If Volatile.Read(_lngIdPre) <> tmp1 And Volatile.Read(_lngIdPre) >= 0 And tmp1 > 0 Then
                    Volatile.Write(_lngIdPre, tmp1)
                    ShowResult() '显示表格添加新测试记录
                    User_AddRecord(_curRow)
                    If g_udtOtherSet.blnAutoSaveWave Then
                        SaveWaveCsv(g_udtOtherSet.strAutoSaveWavePath & g_udtReport.RecordId & ".csv")
                    End If
                    If g_udtOtherSet.blnScanGuns Then
                        _productCodeBuffer = ""
                    End If
                End If
            End SyncLock

            If ucPress = 10 And Volatile.Read(_lngIdPre) = tmp1 Then
                Table_DelBlankLine()
            End If

            If Not Volatile.Read(_lngGroupIdPre) = -1 And Volatile.Read(_lngGroupIdPre) <> uLPU.OUT_ReadUint16(1, ADDR_OUT_GROUP) Then
                log.WriteLogInfo("泄漏检测", "组号不同新增产品")
                SaveProductAndCreateReport()
                RecordList.Table.Clear()
            End If

            If ucPress >= 2 And ucPress <= 8 Then '(2) = "延时1"到(8) = "大漏检测"
                ChartAddPoint() '图表增加一点
            End If

            RepeatDetection()
            ScanRun()
        End Sub

        '=====================================================================
        '函 数 名: RepeatDetection
        '参    数: 
        '返 回 值:
        '功能描述: 重复检测
        '=====================================================================
        Private Sub RepeatDetection()
            If Repeat Then
                If RepeatNum <= 0 Then Repeat = False
                If GetLPU_Process() = 1 And _startMode <> "" Then
                    _lngAutoStartTime += _timer.Interval.Milliseconds
                    Dim interval As Short = RepeatInterval - (_lngAutoStartTime \ 1000)
                    LinkInfoList(0).Interval = "剩余 " & IIf(interval <= 0, 0, interval) & " S"
                Else
                    _lngAutoStartTime = 0
                End If
            Else
                _lngAutoStartTime = 0
            End If


            If Repeat Then
                If _isStart = False Then
                    If GetLPU_Process() = 1 And RepeatNum > 0 And _lngAutoStartTime / 1000 > RepeatInterval Then
                        If _startMode = "BUBL" Or _startMode = "Step" Then
                            Repeat = False
                            Return
                        End If
                        Start()
                        RepeatNum -= 1
                    End If
                End If
            End If

        End Sub

        '=====================================================================
        '函 数 名: ScanRun
        '参    数: 
        '返 回 值:
        '功能描述: 扫码枪运行
        '=====================================================================
        Private Sub ScanRun()
            If g_udtOtherSet.blnScanGuns Then
                If g_serialPort.IsOpen Then
                    If g_serialPort.PortName <> "COM" & (1 + g_udtOtherSet.uintPortName) Then
                        g_serialPort.Close()
                    End If

                    If g_serialPort.BaudRate <> g_strBaudRate(g_udtOtherSet.uintBaudRate) Then
                        g_serialPort.Close()
                    End If
                End If
                If g_serialPort.IsOpen = False Then
                    Try
                        RemoveHandler g_serialPort.DataReceived, AddressOf OnDataReceived
                        g_serialPort.PortName = "COM" & (1 + g_udtOtherSet.uintPortName)
                        g_serialPort.Handshake = Handshake.None
                        g_serialPort.BaudRate = g_strBaudRate(g_udtOtherSet.uintBaudRate)
                        g_serialPort.DataBits = 8
                        g_serialPort.Parity = Parity.None
                        g_serialPort.StopBits = StopBits.One

                        AddHandler g_serialPort.DataReceived, AddressOf OnDataReceived

                        g_serialPort.Open()
                    Catch ex As Exception
                        RemoveHandler g_serialPort.DataReceived, AddressOf OnDataReceived
                        g_udtOtherSet.blnScanGuns = False
                        User_WritePublicValue("扫描枪有效", False, ex.Source)
                        log.WriteLogError("扫码枪启动", ex.Message)
                    End Try
                End If
            Else
                If g_serialPort.IsOpen Then
                    RemoveHandler g_serialPort.DataReceived, AddressOf OnDataReceived
                    g_serialPort.Close()
                End If
            End If
        End Sub


        '=====================================================================
        '函 数 名: OnDataReceived
        '参    数: sender 事件触发对象, e 参数
        '返 回 值:
        '功能描述: 串口接收到数据事件
        '=====================================================================
        Private Sub OnDataReceived(sender As Object, e As SerialDataReceivedEventArgs)
            Dim serialPort = TryCast(sender, SerialPort)
            If IsNothing(serialPort) Then
                Exit Sub
            End If

            Dim tail = g_udtOtherSet.uintEndChar
            Dim isEnd = False
            Try
                Dim str1 As String
                Dim bytesToRead As Integer = serialPort.BytesToRead
                Dim buffer(bytesToRead - 1) As Byte
                serialPort.Read(buffer, 0, bytesToRead)      '实时读取串口信息
                str1 = _productCodeBuffer & Encoding.ASCII.GetString(buffer)
                If str1.Count > 2 Then
                    If tail = 0 Then 'cr
                        If Mid(str1, str1.Count, 1) = vbCr Then
                            str1 = Mid(str1, 1, str1.Count - 1)
                            isEnd = True
                        End If
                    ElseIf tail = 1 Then 'lf
                        If Mid(str1, str1.Count, 1) = vbLf Then
                            str1 = Mid(str1, 1, str1.Count - 1)
                            isEnd = True
                        End If

                    Else 'cr+lf
                        If Mid(str1, str1.Count - 1, 2) = vbCrLf Then
                            str1 = Mid(str1, 1, str1.Count - 2)
                            isEnd = True
                        End If
                    End If
                End If
                Debug.Print("OnDataReceived " & str1)
                DispatcherHelper.CheckBeginInvokeOnUI(Sub()
                                                          ProductCode = str1
                                                          _productCodeBuffer = str1
                                                          If isEnd And g_udtOtherSet.blnAutoStart Then
                                                              Start()
                                                          End If
                                                      End Sub)
            Catch ex As Exception
                If serialPort.IsOpen Then
                    RemoveHandler serialPort.DataReceived, AddressOf OnDataReceived
                    serialPort.DiscardInBuffer()
                    serialPort.Close()
                End If
                g_udtOtherSet.blnScanGuns = False
                User_WritePublicValue("扫描枪有效", False, ex.Source)
                log.WriteLogError("扫码枪数据接收", ex.Message)
            End Try
        End Sub


        '=====================================================================
        '函 数 名: ShowResult
        '参    数: 
        '返 回 值:
        '功能描述: 填充检测结果
        '=====================================================================
        Private Sub ShowResult()

            _curRow(6) = Date.Now()
            If IsDBNull(_curRow(7)) Then
                _curRow(7) = 0
            End If
            _curRow(8) = Format(uLPU.IN_ReadSingle(1, ADDR_IN_RECORD_S_LEAK), g_strEPoint(g_udtOtherSet.uint漏率_Point))
            _curRow(9) = IIf(uLPU.OUT_ReadUint16(1, ADDR_OUT_SDET_UNIT) = 1, g_strGPUnit(g_udtProduct.GpUnitIndex),
                             g_strLeakUnit(g_udtProduct.LeakUnitIndex))
            _curRow(10) = Format(uLPU.IN_ReadSingle(1, ADDR_IN_RECORD_L_LEAK), "0.0")
            _curRow(11) = Format(uLPU.IN_ReadSingle(1, ADDR_IN_RECORD_L_LEAK), "0.0")
            _curRow(12) = "Pa"
            _curRow(13) = Format(uLPU.IN_ReadSingle(1, ADDR_IN_GP1_START), g_strPoint(g_udtOtherSet.uint直压_Point))
            _curRow(14) = Format(uLPU.IN_ReadSingle(1, ADDR_IN_GP2_BALANCE1), g_strPoint(g_udtOtherSet.uint直压_Point))
            If IsDBNull(_curRow(16)) Then
                _curRow(16) = 0.0
            End If
            If IsDBNull(_curRow(17)) Then
                _curRow(17) = 0.0
            End If
            _curRow(18) = Format(uLPU.IN_ReadSingle(1, ADDR_IN_Temperature_SDETECT), "0.0")
            _curRow(19) = g_strPress(uLPU.IN_ReadUint16(1, ADDR_IN_RECORD_PRESS))
            _curRow(20) = g_strTestResult(uLPU.IN_ReadUint16(1, ADDR_IN_RECORD_JUDGE))
            _curRow(21) = "[" & uLPU.IN_ReadUint16(1, ADDR_IN_RECORD_ERR_NO) & "]" &
                          g_strErrorMsg(uLPU.IN_ReadUint16(1, ADDR_IN_RECORD_ERR_NO))
            _curRow(22) = g_strTestType(uLPU.IN_ReadUint16(1, ADDR_IN_TEST_TYPE))

            '判定结果
            Dim tmp As UInt16 = uLPU.IN_ReadUint16(1, ADDR_IN_RECORD_JUDGE)
            ResultBackColorList(0) = IIf(tmp = 0, _okColor, _normalColor)
            ResultBackColorList(1) = IIf(tmp <> 0, _ngColor, _normalColor)
            If tmp <> 0 Then
                NgResult = g_strTestResult(tmp)
            Else
                NgResult = "NG"
            End If

            RecordList = New DataView(_dt)

            Table_DelBlankLine()

        End Sub

        '=====================================================================
        '函 数 名: Operation
        '参    数: 
        '返 回 值:
        '功能描述: 断开/连接Modbus
        '=====================================================================
        Private Async Sub Operation()
            If uLPU.IsServerRunning Then
                StopServer()
                _timer.IsEnabled = uLPU.IsModbusOK()
                EnableButton = uLPU.IsModbusOK()
                EnableStart = uLPU.IsModbusOK()
                _isStart = False
            Else
                OperationText = "连接中"
                Dim result = Await Task.Run(AddressOf StartServer)
                If result = ModbusServer.MODBUS_OK Then
                    g_udtReport.ReportId = Nothing
                    RecordList.Table.Clear()
                    _timer.IsEnabled = uLPU.IsServerRunning()
                End If
            End If
            OperationText = IIf(uLPU.IsServerRunning, "断开", "连接")
        End Sub

        '=====================================================================
        '函 数 名: ShowReportDialog
        '参    数: 
        '返 回 值:
        '功能描述: 弹出新增报告Dialog，根据结果执行后续写入参数和启动检漏
        '=====================================================================
        Private Sub ShowReportDialog()
            Dim productIdList As List(Of String) = User_ListProduct()
            If productIdList.Count <= 0 Then
                log.WriteLogError("查询产品", "当前产品库为空，请先添加产品")
            Else
                If RecordList.Count > 0 Then
                    If MsgBox("是否打印现有记录？", vbYesNo, "保存") = vbYes Then
                        'SaveResult() '保存全部记录
                        PrintResult() '打印全部记录
                    End If
                End If

                Dim reportWindow = New CreateReportWindow()
                reportWindow.ShowDialog()
                If reportWindow.DialogResult Then
                    WriteProduct()
                    While (Not uLPU.OUT_IsAllWrited())
                        Thread.Sleep(50)
                    End While
                    YLabel = " 直压(" & g_strGPUnit(g_udtProduct.GpUnitIndex) & ")"
                    EnableStart = True
                    _dt.Clear()
                    Volatile.Write(_lngGroupIdPre, uLPU.OUT_ReadUint16(1, ADDR_OUT_GROUP))
                    RecordList = New DataView(_dt)
                    ProductName = g_udtProduct.Name
                End If
            End If
        End Sub


        '=====================================================================
        '函 数 名: Table_AddNewLine
        '参    数: 
        '返 回 值:
        '功能描述: 记录表格增加一行
        '=====================================================================
        Private Sub Table_AddNewLine()
            _curRow = _dt.NewRow()

            g_udtReport.RecordId = SystemData.idWorker.nextId()
            _curRow(0) = g_udtReport.ReportId
            _curRow(1) = g_udtProduct.Id
            _curRow(2) = g_udtProduct.Name
            _curRow(3) = g_udtProduct.Model
            _curRow(4) = g_udtReport.ProductCode
            _curRow(5) = g_udtReport.RecordId

            _curRow(15) = g_strGPUnit(g_udtProduct.GpUnitIndex)
            _curRow(23) = uLPU.OUT_ReadUint16(1, ADDR_OUT_GROUP)
            For i = 24 To 34
                _curRow(i) = g_udtProduct.Times(i - 24)
            Next

            For i = 35 To 46
                _curRow(i) = g_udtProduct.Limits(i - 35)
            Next

            _curRow(47) = g_udtProduct.WorkVal
            _curRow(48) = g_udtProduct.MasterVal
            _curRow(49) = g_udtProduct.RevCalc

            _curRow(50) = uLPU.OUT_ReadUint16(1, ADDR_OUT_MID_JUDGE) = 1
            _curRow(51) = uLPU.OUT_ReadUint16(1, ADDR_OUT_USB_SAVE_EN) = 1

            _curRow(52) = g_udtProduct.SOffsetEn
            _curRow(53) = g_udtProduct.SOffsetVal
            _curRow(54) = g_udtProduct.SLinearEn
            _curRow(55) = g_udtProduct.SLinearK
            _curRow(56) = g_udtProduct.SLinearB
            _curRow(57) = g_udtProduct.LOffsetEn
            _curRow(58) = g_udtProduct.LOffsetVal
            _curRow(59) = g_udtProduct.LLinearEn
            _curRow(60) = g_udtProduct.LLinearK
            _curRow(61) = g_udtProduct.LLinearB

            _curRow(62) = g_udtProduct.RevEn
            _curRow(63) = g_udtProduct.RevSave
            _curRow(64) = g_udtProduct.RevUl
            _curRow(65) = g_udtProduct.RevVal
            _curRow(66) = g_udtProduct.RevWeight
            _curRow(67) = g_udtProduct.RevNg

            _curRow(68) = g_udtProduct.FittingMode
            _curRow(69) = g_udtProduct.FittingA
            _curRow(70) = g_udtProduct.FittingB
            _curRow(71) = g_udtProduct.FittingC
            _curRow(72) = g_udtProduct.StandLeak

            _dt.Rows.Add(_curRow)
            RecordList = New DataView(_dt)
        End Sub

        '=====================================================================
        '函 数 名: WriteProduct
        '参    数: 
        '返 回 值:
        '功能描述: 写入组参数
        '=====================================================================
        Private Sub WriteProduct()

            'uLPU.OUT_WriteUint16(1, ADDR_OUT_GROUP, g_udtProduct.GroupId)

            uLPU.OUT_WriteUint16(1, ADDR_OUT_GP_UNIT, g_udtProduct.GpUnitIndex)

            For i = 0 To g_udtProduct.Times.Count - 1
                uLPU.OUT_WriteSingle(1, ADDR_OUT_TIME_DELAY1 + i * 2, g_udtProduct.Times(i))
            Next

            For i = 0 To g_udtProduct.Limits.Count - 1
                uLPU.OUT_WriteSingle(1, ADDR_OUT_LEAD_P_UP + i * 2, g_udtProduct.Limits(i))
            Next

            '容积参数
            uLPU.OUT_WriteSingle(1, ADDR_OUT_WORK_VOL, g_udtProduct.WorkVal)
            uLPU.OUT_WriteSingle(1, ADDR_OUT_MASTER_VOL, g_udtProduct.MasterVal)

            uLPU.OUT_WriteSingle(1, ADDR_OUT_LEAK_VALUE, g_udtProduct.StandLeak)
            uLPU.OUT_WriteSingle(1, ADDR_OUT_NEGATIVE_CALCULATION, IIf(g_udtProduct.RevCalc, 1, 0))

            ' 一致性校准
            uLPU.OUT_WriteUint16(1, ADDR_OUT_SDET_OFFICE_EN, IIf(g_udtProduct.SOffsetEn, 1, 0))
            uLPU.OUT_WriteUint16(1, ADDR_OUT_LDET_OFFICE_EN, IIf(g_udtProduct.LOffsetEn, 1, 0))
            uLPU.OUT_WriteSingle(1, ADDR_OUT_SDET_OFFICE_VALUE, g_udtProduct.SOffsetVal)
            uLPU.OUT_WriteSingle(1, ADDR_OUT_LDET_OFFICE_VALUE, g_udtProduct.LOffsetVal)

            uLPU.OUT_WriteUint16(1, ADDR_OUT_SDET_LINEAR_EN, IIf(g_udtProduct.SLinearEn, 1, 0))
            uLPU.OUT_WriteUint16(1, ADDR_OUT_LDET_LINEAR_EN, IIf(g_udtProduct.LLinearEn, 1, 0))
            uLPU.OUT_WriteSingle(1, ADDR_OUT_SDET_LINEAR_K, g_udtProduct.SLinearK)
            uLPU.OUT_WriteSingle(1, ADDR_OUT_LDET_LINEAR_K, g_udtProduct.LLinearK)
            uLPU.OUT_WriteSingle(1, ADDR_OUT_SDET_LINEAR_B, g_udtProduct.SLinearB)
            uLPU.OUT_WriteSingle(1, ADDR_OUT_LDET_LINEAR_B, g_udtProduct.LLinearB)

            'Fitting
            uLPU.OUT_WriteUint16(1, ADDR_OUT_FITTING_EN, g_udtProduct.FittingMode)
            uLPU.OUT_WriteSingle(1, ADDR_OUT_FITTING_A, g_udtProduct.FittingA)
            uLPU.OUT_WriteSingle(1, ADDR_OUT_FITTING_B, g_udtProduct.FittingB)
            uLPU.OUT_WriteSingle(1, ADDR_OUT_FITTING_C, g_udtProduct.FittingC)

            uLPU.OUT_WriteUint16(1, ADDR_OUT_SDET_UNIT, g_udtProduct.LeakUnitIndex)

            '加权平均修正
            uLPU.OUT_WriteUint16(1, ADDR_OUT_REV_MODE, IIf(g_udtProduct.RevEn, 1, 0))
            uLPU.OUT_WriteUint16(1, ADDR_OUT_REV_SAVE, IIf(g_udtProduct.RevSave, 1, 0))
            uLPU.OUT_WriteSingle(1, ADDR_OUT_REV_NG, g_udtProduct.RevNg)
            uLPU.OUT_WriteSingle(1, ADDR_OUT_REV_LIMIT, g_udtProduct.RevUl)
            uLPU.OUT_WriteSingle(1, ADDR_OUT_REV_DATA, g_udtProduct.RevVal)
            uLPU.OUT_WriteSingle(1, ADDR_OUT_REV_WEIGHT, g_udtProduct.RevWeight)


        End Sub

        '=====================================================================
        '函 数 名: Start
        '参    数: 
        '返 回 值:
        '功能描述: 启动检漏
        '=====================================================================
        Private Sub Start()
            If g_udtReport.ReportId Is Nothing Or g_udtReport.ReportId = "" Then
                log.WriteLogError("启动", "请先[选择产品]！")
                If Repeat Then
                    Repeat = False
                End If
                Exit Sub
            End If

            If Not _isStart Then
                Select Case _startMode
                    Case "Leak"
                        uLPU.OUT_WriteUint16(1, ADDR_OUT_CONTROL1, uLPU.OUT_ReadUint16(1, ADDR_OUT_CONTROL1) Or &H1)
                    Case "StartWithTANK"
                        uLPU.OUT_WriteUint16(1, ADDR_OUT_CONTROL1, uLPU.OUT_ReadUint16(1, ADDR_OUT_CONTROL1) Or &H800)
                    Case "StartWithCAL"
                        uLPU.OUT_WriteUint16(1, ADDR_OUT_CONTROL1, uLPU.OUT_ReadUint16(1, ADDR_OUT_CONTROL1) Or &H80)
                    Case "BUBL"
                        uLPU.OUT_WriteUint16(1, ADDR_OUT_CONTROL1, uLPU.OUT_ReadUint16(1, ADDR_OUT_CONTROL1) Or &H100)
                    Case "Fitting"
                        uLPU.OUT_WriteUint16(1, ADDR_OUT_CONTROL1, uLPU.OUT_ReadUint16(1, ADDR_OUT_CONTROL1) Or &H20)
                    Case "FittingCAL"
                        uLPU.OUT_WriteUint16(1, ADDR_OUT_CONTROL1, uLPU.OUT_ReadUint16(1, ADDR_OUT_CONTROL1) Or &H40)
                    Case "SDET_Offset"
                        uLPU.OUT_WriteUint16(1, ADDR_OUT_CONTROL1, uLPU.OUT_ReadUint16(1, ADDR_OUT_CONTROL1) Or &H200)
                    Case "LDET_Offset"
                        uLPU.OUT_WriteUint16(1, ADDR_OUT_CONTROL1, uLPU.OUT_ReadUint16(1, ADDR_OUT_CONTROL1) Or &H400)
                    Case "CAL1"
                        uLPU.OUT_WriteUint16(1, ADDR_OUT_CONTROL1, uLPU.OUT_ReadUint16(1, ADDR_OUT_CONTROL1) Or &H4)
                    Case "CAL2"
                        uLPU.OUT_WriteUint16(1, ADDR_OUT_CONTROL1, uLPU.OUT_ReadUint16(1, ADDR_OUT_CONTROL1) Or &H8)
                    Case "CAL12"
                        uLPU.OUT_WriteUint16(1, ADDR_OUT_CONTROL1, uLPU.OUT_ReadUint16(1, ADDR_OUT_CONTROL1) Or &H10)
                    Case "Step"
                        uLPU.OUT_WriteUint16(1, ADDR_OUT_CONTROL1, uLPU.OUT_ReadUint16(1, ADDR_OUT_CONTROL1) Or &H1000)
                End Select
                Volatile.Write(_lngIdPre, uLPU.IN_ReadUint16(1, ADDR_IN_RECORD_ID))
                ChartInit()
                g_udtReport.ProductCode = ProductCode
                If g_udtReport.ProductCode = "" Then
                    g_udtReport.ProductCode = "空"
                End If
                Table_AddNewLine()
                ResultBackColorList(0) = _normalColor
                ResultBackColorList(1) = _normalColor
                NgResult = "NG"
                _isStart = True
                Debug.Print("开启检测")
            Else
                If _startMode = "Step" Then
                    uLPU.OUT_WriteUint16(1, ADDR_OUT_CONTROL1, uLPU.OUT_ReadUint16(1, ADDR_OUT_CONTROL1) Or &H1000)
                End If
            End If
        End Sub

        '=====================================================================
        '函 数 名: SaveProductAndCreateReport
        '参    数: 
        '返 回 值:
        '功能描述: 保存组参数（内存）、新建报告
        '=====================================================================
        Public Sub SaveProductAndCreateReport()
            Dim productModel As New ProductModel

            productModel.Id = SystemData.idWorker.nextId()
            productModel.Name = "组号" & uLPU.OUT_ReadUint16(1, ADDR_OUT_GROUP) & "自动添加"
            productModel.Model = "空"

            productModel.GroupId = uLPU.OUT_ReadUint16(1, ADDR_OUT_GROUP)

            For i = 0 To productModel.Times.Count - 1
                productModel.Times(i) = CStr(uLPU.OUT_ReadSingle(1, ADDR_OUT_TIME_DELAY1 + i * 2)) '环节时间
            Next i

            For i = 0 To productModel.Limits.Count - 1
                productModel.Limits(i) = CStr(uLPU.OUT_ReadSingle(1, ADDR_OUT_LEAD_P_UP + i * 2)) '环节时间
            Next i

            productModel.GpUnitIndex = uLPU.OUT_ReadUint16(1, ADDR_OUT_GP_UNIT)
            productModel.LeakUnitIndex = uLPU.OUT_ReadUint16(1, ADDR_OUT_SDET_UNIT)


            productModel.SOffsetEn = uLPU.OUT_ReadUint16(1, ADDR_OUT_SDET_OFFICE_EN) <> 0
            productModel.SOffsetVal = uLPU.OUT_ReadSingle(1, ADDR_OUT_SDET_OFFICE_VALUE)
            productModel.SLinearEn = uLPU.OUT_ReadInt16(1, ADDR_OUT_SDET_LINEAR_EN) <> 0
            productModel.SLinearK = uLPU.OUT_ReadInt16(1, ADDR_OUT_SDET_LINEAR_K) <> 0
            productModel.SLinearB = uLPU.OUT_ReadInt16(1, ADDR_OUT_SDET_LINEAR_B) <> 0

            productModel.LOffsetEn = uLPU.OUT_ReadUint16(1, ADDR_OUT_LDET_OFFICE_EN) <> 0
            productModel.LOffsetVal = uLPU.OUT_ReadSingle(1, ADDR_OUT_LDET_OFFICE_VALUE)
            productModel.LLinearEn = uLPU.OUT_ReadInt16(1, ADDR_OUT_LDET_LINEAR_EN) <> 0
            productModel.LLinearK = uLPU.OUT_ReadInt16(1, ADDR_OUT_LDET_LINEAR_K) <> 0
            productModel.LLinearB = uLPU.OUT_ReadInt16(1, ADDR_OUT_LDET_LINEAR_B) <> 0

            productModel.RevEn = uLPU.OUT_ReadUint16(1, ADDR_OUT_SDET_UNIT) <> 0
            productModel.RevSave = uLPU.OUT_ReadUint16(1, ADDR_OUT_REV_SAVE) <> 0
            productModel.RevUl = CStr(uLPU.OUT_ReadSingle(1, ADDR_OUT_REV_LIMIT))
            productModel.RevVal = CStr(uLPU.OUT_ReadSingle(1, ADDR_OUT_REV_DATA))
            productModel.RevWeight = CStr(uLPU.OUT_ReadSingle(1, ADDR_OUT_REV_WEIGHT))
            productModel.RevNg = CStr(uLPU.OUT_ReadSingle(1, ADDR_OUT_REV_NG))

            productModel.FittingMode = uLPU.OUT_ReadUint16(1, ADDR_OUT_FITTING_EN)
            productModel.FittingA = uLPU.OUT_ReadSingle(1, ADDR_OUT_FITTING_A)
            productModel.FittingB = uLPU.OUT_ReadSingle(1, ADDR_OUT_FITTING_B)
            productModel.FittingC = uLPU.OUT_ReadSingle(1, ADDR_OUT_FITTING_C)

            productModel.WorkVal = uLPU.OUT_ReadSingle(1, ADDR_OUT_WORK_VOL)
            productModel.MasterVal = uLPU.OUT_ReadSingle(1, ADDR_OUT_MASTER_VOL)
            productModel.StandLeak = CStr(uLPU.OUT_ReadSingle(1, ADDR_OUT_LEAK_VALUE))
            productModel.RevCalc = uLPU.OUT_ReadUint16(1, ADDR_OUT_NEGATIVE_CALCULATION) <> 0

            g_udtProduct = productModel
            Volatile.Write(_lngGroupIdPre, productModel.GroupId)

            User_WriteProduct(g_udtProduct, "后端子切换、启动自动添加产品")

            g_udtReport.ReportId = SystemData.idWorker.nextId()
            g_udtReport.ProductId = productModel.Id
            g_udtReport.ProductCode = "空"

            User_AddReport(g_udtReport)
            ProductName = g_udtProduct.Name
        End Sub

        '=====================================================================
        '函 数 名: SaveResult
        '参    数: 
        '返 回 值:
        '功能描述: 弹出文件选择框，保存测试结果CSV
        '=====================================================================
        Public Sub SaveResult()
            If RecordList.Count > 0 Then  '有测试记录
                '用户保存
                Dim saveDialog As New SaveFileDialog With {
                        .Filter = "CSV文件|*.csv|所有文件|*.*",
                        .FileName = ""
                        }
                saveDialog.ShowDialog()

                If saveDialog.FileName <> "" Then
                    SaveResultCsv(saveDialog.FileName)  '保存全部记录
                End If
            Else
                log.WriteLogError("导出记录", "当前无记录数据，无法导出")
            End If
        End Sub

        '=====================================================================
        '函 数 名: SaveResultCsv
        '参    数: strFileName csv文件名
        '返 回 值:
        '功能描述: 将测试结果写入CSV文件
        '=====================================================================
        Private Sub SaveResultCsv(strFileName As String)
            If RecordList.Count > 0 Then  '有测试记录
                Dim swFile = New StreamWriter(strFileName, False, Encoding.Default)

                Dim r As Integer
                Dim c As Integer

                Dim strRow As String

                Try
                    strRow = ""
                    For c = 0 To g_strResultTitle.Count - 1
                        strRow &= g_strResultTitle(c) & ","
                    Next
                    swFile.Write(strRow & vbCrLf)

                    For r = 0 To RecordList.Count - 1
                        strRow = ""
                        For c = 0 To g_strResultTitle.Count - 1
                            If c = 0 Or c = 1 Or c = 4 Or c = 5 Then
                                strRow &= "'" & RecordList(r)(c) & ","
                            Else
                                strRow &= RecordList(r)(c) & ","
                            End If
                        Next c
                        swFile.Write(strRow & vbCrLf)
                    Next r

                Catch ex As Exception
                    log.WriteLogError("SaveResultCsv():" & strFileName, ex.Message)
                End Try
                swFile.Close()
            End If
        End Sub


        '=====================================================================
        '函 数 名: PrintResult
        '参    数: 
        '返 回 值:
        '功能描述: （打印全部记录）
        '=====================================================================
        Private Sub PrintResult()
            If RecordList.Count > 0 Then '有测试记录
                Dim strConn As String = "Provider=" & g_strDatabaseProvider & ";Data Source=" &
                                        g_strDatabasePath & "\测试记录.mdb" &
                                        ";Persist Security Info=False;Jet OLEDB:Database Password=" & SUPER_USER_NAME &
                                        SUPER_PASSWORD

                Dim strCmd As String = "SELECT * FROM 测试记录 where 报告号='" & g_udtReport.ReportId & "'"

                Dim dbAdapter As New OleDbDataAdapter
                Dim dbTable As New DataTable

                Try
                    dbAdapter = New OleDbDataAdapter(strCmd, strConn)
                    dbAdapter.Fill(dbTable)

                    TestReportPrint(g_strExcelPath & "\" & g_udtOtherSet.strTestRecordTemplateName,
                                    g_udtOtherSet.strTestRecordSavePath, dbTable, Nothing)

                Catch ex As Exception
                    log.WriteLogError("打印测试数据", ex.Message)
                    Return
                Finally
                    dbAdapter.Dispose()
                End Try
            Else
                log.WriteLogError("保存报告", "报告无数据，无法导出")
            End If
        End Sub


        '=====================================================================
        '函 数 名: Table_DelBlankLine
        '参    数: 
        '返 回 值:
        '功能描述: 记录表格删除最后一行（没有填完整的记录时）
        '=====================================================================
        Private Sub Table_DelBlankLine()
            If _dt.Rows.Count > 0 Then
                Dim index1 As Integer
                index1 = _dt.Rows.Count - 1

                Dim i As UInt16

                Dim blnNeedDel As Boolean = False

                For i = 0 To g_strResultTitle.Count - 1
                    If IsDBNull(_dt.Rows.Item(index1).Item(i)) Then
                        blnNeedDel = True
                    End If
                Next

                If blnNeedDel Then
                    _dt.Rows.RemoveAt(index1)
                    RecordList = New DataView(_dt)
                End If
            End If
        End Sub

        Public Property CreateReportCommand As RelayCommand
            Get
                Return _createReportCommand
            End Get
            Set(value As RelayCommand)
                _createReportCommand = value
            End Set
        End Property

        Public Property ExportReportCommand As RelayCommand
            Get
                Return _exportReportCommand
            End Get
            Set(value As RelayCommand)
                _exportReportCommand = value
            End Set
        End Property

        Public Property ShowConnectInfoCommand As RelayCommand
            Get
                Return _showConnectInfoCommand
            End Get
            Set(value As RelayCommand)
                _showConnectInfoCommand = value
            End Set
        End Property

        Public Property ConnectCommand As RelayCommand
            Get
                Return _connectCommand
            End Get
            Set(value As RelayCommand)
                _connectCommand = value
            End Set
        End Property

        Public Property StartCommand As RelayCommand
            Get
                Return _startCommand
            End Get
            Set(value As RelayCommand)
                _startCommand = value
            End Set
        End Property

        Public Property StopCommand As RelayCommand
            Get
                Return _stopCommand
            End Get
            Set(value As RelayCommand)
                _stopCommand = value
            End Set
        End Property

        Public Property OperationText As String
            Get
                Return _operationText
            End Get
            Set(value As String)
                _operationText = value
                RaisePropertyChanged()
            End Set
        End Property

        Public Property Gp1 As String
            Get
                Return _gp1
            End Get
            Set(value As String)
                _gp1 = value
                RaisePropertyChanged()
            End Set
        End Property

        Public Property Gp2 As String
            Get
                Return _gp2
            End Get
            Set(value As String)
                _gp2 = value
                RaisePropertyChanged()
            End Set
        End Property

        Public Property Dp As String
            Get
                Return _dp
            End Get
            Set(value As String)
                _dp = value
                RaisePropertyChanged()
            End Set
        End Property

        Public Property Temp As String
            Get
                Return _temp
            End Get
            Set(value As String)
                _temp = value
                RaisePropertyChanged()
            End Set
        End Property

        Public Property Leak As String
            Get
                Return _leak
            End Get
            Set(value As String)
                _leak = value
                RaisePropertyChanged()
            End Set
        End Property

        Public Property ErrorCode As String
            Get
                Return _errorCode
            End Get
            Set(value As String)
                _errorCode = value
                RaisePropertyChanged()
            End Set
        End Property

        Public Property LinkInfoList As ObservableCollection(Of LinkModel)
            Get
                Return _linkInfoList
            End Get
            Set(value As ObservableCollection(Of LinkModel))
                _linkInfoList = value
            End Set
        End Property

        Public Property StateFontColorList As ObservableCollection(Of Brush)
            Get
                Return _stateFontColorList
            End Get
            Set(value As ObservableCollection(Of Brush))
                _stateFontColorList = value
            End Set
        End Property

        Public Property StateBackColorList As ObservableCollection(Of Brush)
            Get
                Return _stateBackColorList
            End Get
            Set(value As ObservableCollection(Of Brush))
                _stateBackColorList = value
            End Set
        End Property

        Public Property ResultBackColorList As ObservableCollection(Of Brush)
            Get
                Return _resultBackColorList
            End Get
            Set(value As ObservableCollection(Of Brush))
                _resultBackColorList = value
            End Set
        End Property

        Public Property RecordList As DataView
            Get
                Return _recordList
            End Get
            Set(value As DataView)
                _recordList = value
            End Set
        End Property

        Public Property StartModeCommand As RelayCommand(Of String)
            Get
                Return _startModeCommand
            End Get
            Set(value As RelayCommand(Of String))
                _startModeCommand = value
            End Set
        End Property

        Public Property ChartModel As PlotModel
            Get
                Return _chartModel
            End Get
            Set(value As PlotModel)
                _chartModel = value
                RaisePropertyChanged()
            End Set
        End Property

        Public Property SaveWaveCommand As RelayCommand
            Get
                Return _saveWaveCommand
            End Get
            Set(value As RelayCommand)
                _saveWaveCommand = value
            End Set
        End Property

        Public Property EnableButton As Boolean
            Get
                Return _enableButton
            End Get
            Set
                If _enableButton <> Value Then
                    _enableButton = Value
                    RaisePropertyChanged()
                End If
            End Set
        End Property

        Public Property ZeroCommand As RelayCommand
            Get
                Return _zeroCommand
            End Get
            Set
                _zeroCommand = Value
            End Set
        End Property

        Public Property ClearErrorCommand As RelayCommand
            Get
                Return _clearErrorCommand
            End Get
            Set
                _clearErrorCommand = Value
            End Set
        End Property

        Public Property Repeat As Boolean
            Get
                Return _repeat
            End Get
            Set
                _repeat = Value
                RaisePropertyChanged()
            End Set
        End Property

        Public Property RepeatNum As UShort
            Get
                Return _repeatNum
            End Get
            Set
                _repeatNum = Value
                RaisePropertyChanged()
            End Set
        End Property

        Public Property NgResult As String
            Get
                Return _ngResult
            End Get
            Set
                If _ngResult <> Value Then
                    _ngResult = Value
                    RaisePropertyChanged()
                End If
            End Set
        End Property

        Public Property DpColor As Brush
            Get
                Return _dpColor
            End Get
            Set
                _dpColor = Value
                RaisePropertyChanged()
            End Set
        End Property

        Public Property RepeatInterval As UShort
            Get
                Return _repeatInterval
            End Get
            Set
                _repeatInterval = Value
            End Set
        End Property

        Public Property EnableStart As Boolean
            Get
                Return _enableStart
            End Get
            Set
                _enableStart = Value
                RaisePropertyChanged()
            End Set
        End Property

        Public Property ProductName As String
            Get
                Return _productName
            End Get
            Set
                _productName = Value
                RaisePropertyChanged()
            End Set
        End Property

        Public Property ProductCode As String
            Get
                Return _productCode
            End Get
            Set
                _productCode = Value
                RaisePropertyChanged()
            End Set
        End Property

        Public Property GpPoints As ObservableCollection(Of DataPoint)
            Get
                Return _gpPoints
            End Get
            Set
                _gpPoints = Value
            End Set
        End Property

        Public Property DpPoints As ObservableCollection(Of DataPoint)
            Get
                Return _dpPoints
            End Get
            Set
                _dpPoints = Value
            End Set
        End Property

        Public Property MinY1 As Double
            Get
                Return _minY1
            End Get
            Set
                _minY1 = Value
                RaisePropertyChanged()
            End Set
        End Property

        Public Property MaxY1 As Double
            Get
                Return _maxY1
            End Get
            Set
                _maxY1 = Value
                RaisePropertyChanged()
            End Set
        End Property

        Public Property MinY2 As Double
            Get
                Return _minY2
            End Get
            Set
                _minY2 = Value
                RaisePropertyChanged()
            End Set
        End Property

        Public Property MaxY2 As Double
            Get
                Return _maxY2
            End Get
            Set
                _maxY2 = Value
                RaisePropertyChanged()
            End Set
        End Property

        Public Property YLabel As String
            Get
                Return _yLabel
            End Get
            Set
                _yLabel = Value
                RaisePropertyChanged()
            End Set
        End Property
    End Class
End Namespace